/* Copyright (c) 2022-2022, LiWangQian<liwangqian@huawei.com> All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef LOGPP_BASIC_LOGGER_H
#define LOGPP_BASIC_LOGGER_H

#include "logpp/configs.h"
#include "logpp/utils/level.h"
#include "logpp/utils/context.h"
#include "logpp/utils/memory.h"
#include "logpp/utils/message.h"
#include "logpp/sinks/isink.h"
#include "logpp/filters/ifilter.h"

#include <cstdarg>

LOGPP_NS_BEGIN

template <typename Filter = ifilter, typename Sink = isink>
class basic_logger {
public:
    using filter_type = Filter;
    using sink_type = Sink;

    ~basic_logger() = default;
    basic_logger(Filter *filter, Sink *sink)
        : filter_{filter}
        , sink_{sink}
    {}

    void log(logpp::level lv, const char *file, uint16_t line, const char *fmt, ...) noexcept
    {
        va_list args;
        va_start(args, fmt);
        vlog(lv, file, line, fmt, args);
        va_end(args);
    }

    void vlog(logpp::level lv, const char *file, uint16_t line, const char *fmt, va_list args) noexcept
    {
        logpp::context ctx{file, line, lv};
        logpp::message msg;
        msg.vformat(ctx, fmt, args);
        if (!filter_ || !filter_->filter(msg)) {
            if (sink_ != nullptr) {
                sink_->open();
                sink_->output(msg);
                sink_->flush();
                sink_->close();
            }
        }
    }

    logpp::level get_level() const noexcept
    {
        return lv_;
    }

    void set_level(logpp::level lv) noexcept
    {
        lv_ = lv;
    }

    bool level_enabled(logpp::level lv) const noexcept
    {
        return lv >= lv_;
    }

    const filter_type *get_filter() const noexcept
    {
        return filter_;
    }

    filter_type *get_filter() noexcept
    {
        return filter_;
    }

    const sink_type *get_sink() const noexcept
    {
        return sink_;
    }

    sink_type *get_sink() noexcept
    {
        return sink_;
    }

private:
    filter_type *filter_{nullptr};
    sink_type *sink_{nullptr};
    logpp::level lv_{logpp::level::LV_VERBOSE};
};

template <typename Filter, typename Sink>
static inline auto make_logger(Filter *filter, Sink *sink)
{
    return new_object<basic_logger<Filter, Sink>>(filter, sink);
}

LOGPP_NS_END

#endif
